#include "dechexbin.h"

static gint open_flag;
/*Toggle button*/
static const gchar * but_label1 = "Input number 2";
static const gchar * but_label2 = "Close number 2";
/*Toggle button*/
GtkWidget* but = NULL;

/*Frame 3*/
GtkWidget* f3 = NULL;

/*Result window*/
GtkWidget* w2 = NULL;

/*op frame*/
GtkWidget* op_f = NULL;

char op_flag = ADD;

static unsigned int num_to_le(gchar * le_str,long long int x)
{
	do {
		long long int tmp;
		tmp = x & 0xFF;
		if(tmp <= 0xF){
			sprintf(le_str,"0%llX",tmp);
		} else {
			sprintf(le_str,"%llX",tmp);
		}
		le_str += 2;
	} while( x >>= 8 );
	return 1;
}

/*Change a number to a little endian binary string representation*/
static unsigned int num_to_le_bin(gchar * le_str, long long int x)
{
	gchar * tmp;
	do {
		int i;
		le_str += 8;
		tmp = le_str - 1;
		for(i = 0; i <= 7; i++){
			if((x >> i) & 0x01){
				*tmp = '1';
			} else {
				*tmp = '0';
			}
			tmp--;
		}
	} while( x >>= 8 );
	*le_str = '\0';
	return 1;
}

/*Change a number to a big endian binary string representation*/
static unsigned int num_to_be_bin(gchar * be_str, long long int x)
{
	
	num_to_le_bin(be_str,x);
/*
	printf("%s\n",be_str);
*/
	gchar tmp[8];
	int slen = strlen(be_str);
/*
	printf("%d\n",slen);
*/
	int round = slen / (2 * 8);
	int i;
	for(i = 0; i < round; i++){
		strncpy(tmp,be_str + i * 8,8);
		strncpy(be_str + i * 8,be_str + slen - ( i + 1 ) * 8, 8);
		strncpy(be_str + slen - ( i + 1 ) * 8, tmp, 8);
	}
/*
	printf("%s\n",be_str);
*/
	return 1;
}

static void set_each_entry_text(n_entry * self)
{
	gchar str[40],hlestr[40],hbestr[40],blestr[160],bbestr[160];
	num_to_le(hlestr,self->value);
	num_to_le_bin(blestr,self->value);
	num_to_be_bin(bbestr,self->value);
	sprintf(str,"%lld",self->value);
	sprintf(hbestr,"%llX",self->value);
	gtk_entry_set_text(GTK_ENTRY(self->dec_entry),str);
	gtk_entry_set_text(GTK_ENTRY(self->hex_be_entry),hbestr);
	gtk_entry_set_text(GTK_ENTRY(self->hex_le_entry),hlestr);
	gtk_entry_set_text(GTK_ENTRY(self->bin_le_entry),blestr);
	gtk_entry_set_text(GTK_ENTRY(self->bin_be_entry),bbestr);
}

static void set_entry_text(n_entry * self, n_entry * res,int base,GtkEntry * e)
{
	self->value = strtoll(gtk_entry_get_text(e),NULL,base);
	set_each_entry_text(self);
#if 0
	gchar str[40],hlestr[40],hbestr[40],blestr[160],bbestr[160];
	num_to_le(hlestr,self->value);
	num_to_le_bin(blestr,self->value);
	num_to_be_bin(bbestr,self->value);
	switch(base){
		case 0: /*modify the hex&bin entry*/
			sprintf(hbestr,"%llX",self->value);
			gtk_entry_set_text(GTK_ENTRY(self->hex_be_entry),hbestr);
			gtk_entry_set_text(GTK_ENTRY(self->hex_le_entry),hlestr);
			gtk_entry_set_text(GTK_ENTRY(self->bin_le_entry),blestr);
			gtk_entry_set_text(GTK_ENTRY(self->bin_be_entry),bbestr);
			break;
		case 16: /*modify the dec,hex_le,hex_be,bin entry*/
			sprintf(str,"%lld",self->value);
			gtk_entry_set_text(GTK_ENTRY(self->dec_entry),str);
			gtk_entry_set_text(GTK_ENTRY(self->hex_le_entry),hlestr);
			gtk_entry_set_text(GTK_ENTRY(self->bin_le_entry),blestr);
			gtk_entry_set_text(GTK_ENTRY(self->bin_be_entry),bbestr);
			break;
		default:
			perror("BUG");
			exit(1);
	}
#endif
}

static void entry_change(GtkEntry * e,event_data_t * data)
{
	if(NULL == data){
		printf("data is NULL\n");
		exit(1);
	}
	switch(data->type){
		case n1d:
			set_entry_text(data->n1,data->n3,0,e);
			break;
		case n1h:
			set_entry_text(data->n1,data->n3,16,e);
			break;
		case n2d:
			set_entry_text(data->n2,data->n3,0,e);
			break;
		case n2h:
			set_entry_text(data->n2,data->n3,16,e);
			break;
		default:
			perror("Internal ERROR, exit!");
			exit(1);
	}
}

static void op_change(GtkRadioButton * rb, gpointer all)
{
	n_entry * n1, * n2, * n3;
	n1 = ((n_entry **)all)[0];
	n2 = ((n_entry **)all)[1];
	n3 = ((n_entry **)all)[2];
	gchar * label = gtk_button_get_label(GTK_BUTTON(rb));
	switch(label[0]){
		case '+':
			op_flag = ADD;
			n3->value = n1->value + n2->value;
			break;
		case '-':
			op_flag = SUB;
			n3->value = n1->value - n2->value;
			break;
		case 'x':
			op_flag = MUL;
			n3->value = n1->value * n2->value;
			break;
		case '/':
			op_flag = DIV;
			n3->value = n1->value / n2->value;
			break;
		default:
			perror("BUG");
			exit(1);
	}
	set_each_entry_text(n3);
}

static void sig_connect(n_entry * n1, n_entry * n2, n_entry * n3,event_data_t * ed)
{
	g_signal_connect(G_OBJECT(n1->dec_entry),"changed",G_CALLBACK \
			(entry_change),&ed[0]);
	g_signal_connect(G_OBJECT(n1->hex_be_entry),"changed",G_CALLBACK \
			(entry_change),&ed[1]);

	g_signal_connect(G_OBJECT(n2->dec_entry),"changed",G_CALLBACK \
			(entry_change),&ed[2]);
	g_signal_connect(G_OBJECT(n2->hex_be_entry),"changed",G_CALLBACK \
			(entry_change),&ed[3]);
}
static void sig_connect_opbutton(GtkWidget * button_add, GtkWidget * button_sub, \
				GtkWidget * button_mul,GtkWidget * button_div, \
				n_entry ** all)
{
	g_signal_connect(G_OBJECT(button_add),"pressed",G_CALLBACK \
			(op_change),all);
	g_signal_connect(G_OBJECT(button_sub),"pressed",G_CALLBACK \
			(op_change),all);
	g_signal_connect(G_OBJECT(button_mul),"pressed",G_CALLBACK \
			(op_change),all);
	g_signal_connect(G_OBJECT(button_div),"pressed",G_CALLBACK \
			(op_change),all);
}

static void callback( GtkWidget *widget,
		gpointer   win )
{
	GtkWindow * w;
	w  = (GtkWindow *) win;
	gint width,height;
	gtk_window_get_size(w,&width,&height);
	switch(open_flag){
		case FALSE:
			gtk_window_resize(w,width,height + 110);
			open_flag = TRUE;
			gtk_widget_show (f3);
			gtk_widget_show (w2);
			gtk_widget_show (op_f);
			gtk_button_set_label(GTK_BUTTON(but),but_label2);
			break;
		case TRUE:
			gtk_window_resize(w,width,height - 110);
			open_flag = FALSE;
			gtk_widget_hide (f3);
			gtk_widget_hide (w2);
			gtk_widget_hide (op_f);
			gtk_button_set_label(GTK_BUTTON(but),but_label1);
			break;
		default:
			break;
	}
	/* test bit */
	/*
	   g_printf("ADD is %d\n",ADD);
	   g_printf("SUB is %d\n",SUB);
	   g_printf("MUL is %d\n",MUL);
	   g_printf("DIV is %d\n",DIV);
	   */
}


int main(int argc, char *argv[] )
{
	gtk_init (&argc, &argv);
	GtkWidget *window;

	open_flag = FALSE;

	GtkClipboard * clip;
	GdkDisplay* dis;
	dis = gdk_display_open (":0.0");
	if (dis == NULL){
		fprintf (stderr,"Cannot get display ! \n");
		exit(1);
	}
	clip = gtk_clipboard_get_for_display (dis,GDK_SELECTION_PRIMARY);
	/*	clip = gtk_clipboard_get (GDK_SELECTION_PRIMARY); */
	if (clip == NULL){
		fprintf (stderr, "Cannot get clipboard ! \n");
		exit(1);
	}

	gint width,height;
	gchar* title;

	title = "DecHexBin viewer";
	/*This is the Golden seperation*/
	width = 400;
	height = 165;

	window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	g_signal_connect (G_OBJECT (window), "delete_event",
			G_CALLBACK (gtk_main_quit), NULL);

	g_signal_connect (G_OBJECT (window), "destroy",
			G_CALLBACK (gtk_main_quit), NULL);

	/* Sets the border width of the window. */
	gtk_container_set_border_width (GTK_CONTAINER (window), 5);
	gtk_window_set_title (GTK_WINDOW(window),title);
	gtk_widget_set_size_request (window,width,height);
	gtk_window_set_position(GTK_WINDOW(window),GTK_WIN_POS_CENTER);
	gtk_widget_show (window);

	/*try to create another windows
	 * this is the result window*/
	GtkWidget *windows2;
	windows2 = gtk_window_new (GTK_WINDOW_TOPLEVEL);
	gtk_window_set_title (GTK_WINDOW(windows2),"Results");
	gtk_widget_set_size_request (windows2,width,100);
	gtk_window_set_resizable(GTK_WINDOW(windows2),FALSE);
	gtk_widget_hide (windows2);
	GtkWidget *result_frame;
	result_frame = gtk_frame_new("");
	gtk_widget_show(result_frame);
	gtk_container_add(GTK_CONTAINER(windows2),result_frame);
	/* No [X] button anymore */
	gtk_window_set_deletable (GTK_WINDOW(windows2),FALSE);
	w2 = windows2;

	/*Create a 3 frame vbox
	 * first frame is the input frame
	 * second frame is a toggle button & operation radio buttons
	 * third frame is another input frame used in operation*/
	GtkWidget *main_frame;
	main_frame = gtk_vbox_new(FALSE, 2);
	gtk_widget_show (main_frame);
	gtk_container_add(GTK_CONTAINER(window), main_frame);

	/*Create 3 frames here*/
	GtkWidget *frame1, *frame2, *frame3;
	frame1 = gtk_frame_new("Number 1:");
	gtk_widget_show (frame1);
	frame2 = gtk_frame_new("Calculation");
	gtk_widget_show (frame2);
	frame3 = gtk_frame_new("Number 2:");
	f3 = frame3;

	/*Pack 3 frames in to the main_frame*/
	gtk_box_pack_start(GTK_BOX(main_frame), frame1, FALSE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(main_frame), frame2, FALSE, TRUE, 0);
	gtk_box_pack_start(GTK_BOX(main_frame), frame3, FALSE, TRUE, 0);



	/* Insert a button in frame2 */
	GtkWidget * frame2_hbox;
	frame2_hbox = gtk_hbox_new(FALSE, 2);
	gtk_container_add (GTK_CONTAINER (frame2), frame2_hbox);
	gtk_widget_show(frame2_hbox);

	GtkWidget *button;
	button = gtk_button_new_with_label(but_label1);
	gtk_widget_show (button);
	but = button;
	g_signal_connect (G_OBJECT (button), "clicked",
			G_CALLBACK (callback), (gpointer) window );

	GdkColor color;
	gdk_color_parse ("red", &color);
	gtk_widget_modify_bg (button, GTK_STATE_NORMAL, &color);
	/* Add button to frame2_hbox*/
	gtk_box_pack_start(GTK_BOX(frame2_hbox), button, TRUE, TRUE, 0);

	/* Create an operation frame to contain radio buttons in frame2_hbox*/
	GtkWidget * op_frame;
	op_frame = gtk_frame_new("ops");
	gtk_box_pack_start(GTK_BOX(frame2_hbox), op_frame, TRUE, TRUE, 0);
	op_f = op_frame;


	GtkWidget *button_add, *button_sub, *button_mul, *button_div;
	button_add = gtk_radio_button_new_with_label(NULL,"+");
	button_sub = gtk_radio_button_new_with_label_from_widget(
			GTK_RADIO_BUTTON (button_add), 
			"-");
	button_mul = gtk_radio_button_new_with_label_from_widget(
			GTK_RADIO_BUTTON (button_sub), 
			"x");
	button_div = gtk_radio_button_new_with_label_from_widget(
			GTK_RADIO_BUTTON (button_mul), 
			"/");
	/* Used to place 4 operation radio buttons */
	GtkWidget *op_hbox;
	op_hbox = gtk_hbox_new(FALSE,4);
	gtk_container_add(GTK_CONTAINER(op_frame), op_hbox);
	gtk_widget_show(op_hbox);

	gtk_box_pack_start(GTK_BOX(op_hbox), button_add, TRUE, TRUE, 0);
	gtk_widget_show(button_add);
	gtk_box_pack_start(GTK_BOX(op_hbox), button_sub, TRUE, TRUE, 0);
	gtk_widget_show(button_sub);
	gtk_box_pack_start(GTK_BOX(op_hbox), button_mul, TRUE, TRUE, 0);
	gtk_widget_show(button_mul);
	gtk_box_pack_start(GTK_BOX(op_hbox), button_div, TRUE, TRUE, 0);
	gtk_widget_show(button_div);


	/*test the second button*/
	/* This is disallowd by gtk, must use a box to contain
	 * multiply buttons(widgets).
	 *
	 GtkWidget *b1;
	 b1 = gtk_button_new();
	 gtk_widget_show (b1);
	 gtk_container_add (GTK_CONTAINER (window), b1);
	 */
	n_entry * n1, * n2, * n3;
	n1 = create_number1_entry(frame1);
	n2 = create_number2_entry(frame3);
	n3 = create_result_entry(result_frame);
	set_each_entry_text(n1);
	set_each_entry_text(n2);
	set_each_entry_text(n3);

	/* Assign the callbacks */
	event_data_t * ed  = malloc(4 * sizeof(event_data_t));
	if (NULL == ed){
		perror("malloc event_data_t");
		exit(1);
	}
	EVENT_DATA(ed[0],n1d);	
	EVENT_DATA(ed[1],n1h);	
	EVENT_DATA(ed[2],n2d);	
	EVENT_DATA(ed[3],n2h);	
	sig_connect(n1,n2,n3,ed);
	n_entry * all_entry[3] = {n1,n2,n3};
	sig_connect_opbutton(button_add,button_sub,button_mul,button_div,all_entry);

	gtk_main ();
	free(ed);
	return 0;
}

